/**
 * Basic logic for the internal representation of a graph
 */
GraphApp = (function () {
	var Graph, Node, Vector;
	
	// Helps keeping a nodes position organized
	Vektor = function (x, y) {
		this.x = x;
		this.y = y;
	};
	
	/**
	 * Representation of a single node of a graph
     */ 
	Node = function (value, id, x, y) {
		this.id = id;
		this.pos = new Vektor(x, y);
		this.value = value;
		this.children = [];	
	};
	
	Node.prototype = {
		/**
		 * Removes the edge from this node to the node specified by {id}
		 */
		_removeEdge : function (id) {
			for (var i = this.children.length - 1; i >= 0; i--) {
				if (this.children[i] === id) {
					this.children.splice(i, 1);
					//break;
				}
			}	
		},
		
		/**
		 * Updates the x and y coordinates of the node
		 */
		_setCoordinates : function (x, y) {
			this.pos = new Vektor(x, y);
		},
		
		/**
		 * Adds an edge from this node to the node with the id specified by {to}
		 * TODO: check if already there. Allow edge to node itself?
		 */
		_addEdge : function (to) {
			this.children.push(to);
		},
		
		/**
		 * Returns the array of children for this node, which contains the ids of
		 * the nodes to which an edge leads to from this node
		 */
		_neighbors : function () {
			return this.children;
		},	
	};
	
	/**
	 * Representation of a complete graph
	 */
	Graph = function () {
		this.nodes = {};
		// An autoincremented id to be able to assign each node an unique id
		this._nodeId = 0;
	};
	
	Graph.prototype = {
		/**
		 * Returns the next (within one graph) unique id
		 */
		_getNodeId : function () {
			this._nodeId = this._nodeId + 1;
			return this._nodeId;
		},
		
		/**
		 * Given a value and its coordinates add a new node to the graph
		 * On adding a node the node is assigned with a unique id.
		 * @return the node created by this function
		 * TODO: error handling, default coordinates
		 */
		addNode : function (value, x, y) {
			var n = new Node(value, this._getNodeId(), x, y);
			if (typeof this.nodes[n.id] === "undefined") {
				this.nodes[n.id] = n;
				return n;
			}
		},
		
		/**
		 * Removes the node specified by {id} from the graph. Also removes
		 * all edges in the graph which point to the node.
		 * TODO: error handling (id does not exist)
		 */
		removeNode : function (id) {
			if (typeof this.nodes[id] !== "undefined") {
				this._removeEdgesTo(id);
				delete this.nodes[id];
			} else {
				// throw error
			}
		},
		
		/**
		 * Removes all edges from the graph which point to the node specified
		 * by {id}
		 */
		_removeEdgesTo : function (id) {
			for (var key in this.nodes) {
				this.nodes[key]._removeEdge(id);
			}
		},
		
		/**
		 * Update coordinates for the node specified by {id} to {x}/{y}
		 */
		setCoordinates : function (id, x, y) {
			// validate input?
			if (typeof this.nodes[id] !== "undefined") {
				this.nodes[id]._setCoordinates(x, y);
			} else {
				// throw error, node not in GraphApp.Graph
			}
		},
		
		/**
		 * Add an edge to the graph pointing from the node specified by
		 * {idfrom} to the node with {idto}
		 * TODO: enough?
		 */
		addEdge : function (idfrom, idto) {
			if (this.nodes[idfrom] && this.nodes[idto]) {
				this.nodes[idfrom]._addEdge(idto);
			} else {
				// throw error, at least one id not in GraphApp.Graph
			}
		},
		
		/**
		 * Removes the edge from the node with id {idfrom} pointing to the node
		 * with id {idto}
		 * TODO: error handlig
		 */
		removeEdge : function (idfrom, idto) {
			if (typeof this.nodes[idfrom] !== "undefined" && typeof this.nodes[idto] !== "undefined") {
				this.nodes[idfrom]._removeEdge(idto);
			} else {
				// throw error, at least one id not in GraphApp.Graph
			}	
		},
		
		/**
		 * Returns the array with the node ids to which the node with {id} points
		 * to
		 */
		neighbors : function (id) {
			if (typeof this.nodes[id] !== "undefined") {
				return this.nodes[id]._neighbors();
			} else {
				// throw error, node not in GraphApp.Graph
			}		
		}

		// TODO: Import, Export
	};
	
	return {
		Graph: Graph
	};
}());
